package com.googlecode.ddlutilstool.gui;

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.FlowLayout;
import java.awt.Frame;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.HeadlessException;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.io.IOException;
import java.util.Set;

import javax.swing.DefaultListModel;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.ListCellRenderer;
import javax.swing.ListSelectionModel;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;

import com.googlecode.ddlutilstool.common.DriverBean;
import com.googlecode.ddlutilstool.common.GuiNameAccessor;
import com.googlecode.ddlutilstool.kernel.GuiManager;
import com.googlecode.ddlutilstool.kernel.ProfileManager;

/**
 * <p>A GUI Dialog for editing the drivers.ini file.</p>
 *
 * <p>From this dialog, drivers can be added, deleted or updated.</p>
 */
@SuppressWarnings("serial")
public class DriverDialog extends JDialog implements ActionListener,ListSelectionListener,WindowListener {
	
	private static int DISPLAY_WIDTH = 500;
	private static int DISPLAY_HEIGHT = 230;
	
	private GuiNameAccessor mGuiAccessor;
	private ProfileManager mProfileManager;
	
	private DefaultListModel mDriverListModel;
	private JList mDriverDropDown;

	private JTextField mDriverField;
	private JTextField mClassnameField;
	private JTextField mUrlHintField;
	private JButton mSaveButton;
	private JButton mCancelButton;
	private JButton mDeleteButton;
	private JButton mNewButton;
	
	private DriverBean mDriver=null;
	private int mDriverIndex=-1;
	
	/**
	 * @throws java.awt.HeadlessException
	 */
	public DriverDialog(Frame pParent) throws HeadlessException {
		super(pParent,true);
		mGuiAccessor = new GuiNameAccessor();
		mProfileManager = ProfileManager.getInstance();
		
		setTitle(mGuiAccessor.getString("driver.dialog.title"));
		getContentPane().setLayout(new BorderLayout());
		
		//create the title
		JPanel titlePanel = new JPanel();
		titlePanel.setLayout(new FlowLayout(FlowLayout.CENTER));
		titlePanel.add(new JLabel(mGuiAccessor.getString("driver.dialog.title")));
		getContentPane().add(titlePanel,BorderLayout.NORTH);
		

		JPanel mainPanel= new JPanel();
		mainPanel.setLayout(new GridBagLayout());
		GridBagConstraints c;
		
		//create the list
		mDriverListModel = new DefaultListModel();
		mDriverDropDown = new JList(mDriverListModel);
		mDriverDropDown.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
		mDriverDropDown.addListSelectionListener(this);
		mDriverDropDown.addMouseListener(new MouseHandler());
		mDriverDropDown.setCellRenderer(new ListItemRenderer());
		
		c = new GridBagConstraints();
		c.anchor=GridBagConstraints.FIRST_LINE_START;
		c.fill=GridBagConstraints.BOTH;
		c.weightx=0.3;
		c.gridwidth=1;
		c.gridheight=6;
		c.gridx=0;
		c.gridy=0;
		mainPanel.add(new JScrollPane(mDriverDropDown),c);
		
		//create the form
		JLabel label = new JLabel(mGuiAccessor.getString("driver.dialog.type"));
		c = new GridBagConstraints();
		c.anchor=GridBagConstraints.FIRST_LINE_START;
		c.weightx=0.7;
		c.gridwidth=1;
		c.gridheight=1;
		c.gridx=1;
		c.gridy=0;
		c.insets=new Insets(2,2,0,2);
		mainPanel.add(label,c);
		mDriverField = new JTextField();
		c = new GridBagConstraints();
		c.anchor=GridBagConstraints.FIRST_LINE_START;
		c.fill=GridBagConstraints.HORIZONTAL;
		c.weightx=0.7;
		c.gridwidth=1;
		c.gridheight=1;
		c.gridx=1;
		c.gridy=1;
		c.insets=new Insets(2,2,0,2);
		mainPanel.add(mDriverField,c);
		
		label = new JLabel(mGuiAccessor.getString("driver.dialog.class"));
		c = new GridBagConstraints();
		c.anchor=GridBagConstraints.FIRST_LINE_START;
		c.weightx=0.7;
		c.gridwidth=1;
		c.gridheight=1;
		c.gridx=1;
		c.gridy=2;
		c.insets=new Insets(2,2,0,2);
		mainPanel.add(label,c);
		mClassnameField = new JTextField();
		c = new GridBagConstraints();
		c.anchor=GridBagConstraints.FIRST_LINE_START;
		c.fill=GridBagConstraints.HORIZONTAL;
		c.weightx=0.7;
		c.gridwidth=1;
		c.gridheight=1;
		c.gridx=1;
		c.gridy=3;
		c.insets=new Insets(2,2,0,2);
		mainPanel.add(mClassnameField,c);
		
		label = new JLabel(mGuiAccessor.getString("driver.dialog.hint"));
		c = new GridBagConstraints();
		c.anchor=GridBagConstraints.FIRST_LINE_START;
		c.weightx=0.7;
		c.gridwidth=1;
		c.gridheight=1;
		c.gridx=1;
		c.gridy=4;
		c.insets=new Insets(2,2,0,2);
		mainPanel.add(label,c);
		mUrlHintField = new JTextField();
		c = new GridBagConstraints();
		c.anchor=GridBagConstraints.FIRST_LINE_START;
		c.fill=GridBagConstraints.HORIZONTAL;
		c.weightx=0.7;
		c.gridwidth=1;
		c.gridheight=1;
		c.gridx=1;
		c.gridy=5;
		c.insets=new Insets(2,2,0,2);
		mainPanel.add(mUrlHintField,c);
		
		
		getContentPane().add(mainPanel,BorderLayout.CENTER);

		
		
		
		//create the buttons
		JPanel buttonPanel = new JPanel();
		buttonPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));
		mNewButton = new JButton(mGuiAccessor.getImageIcon("driver.dialog.new.img"));
		mNewButton.setToolTipText(mGuiAccessor.getString("driver.dialog.new"));
		mNewButton.addActionListener(this);
		buttonPanel.add(mNewButton);
		mDeleteButton = new JButton(mGuiAccessor.getImageIcon("driver.dialog.delete.img"));
		mDeleteButton.setToolTipText(mGuiAccessor.getString("driver.dialog.delete"));
		mDeleteButton.addActionListener(this);
		buttonPanel.add(mDeleteButton);
		mSaveButton = new JButton(mGuiAccessor.getImageIcon("driver.dialog.save.img"));
		mSaveButton.setToolTipText(mGuiAccessor.getString("driver.dialog.save"));
		mSaveButton.addActionListener(this);
		buttonPanel.add(mSaveButton);
		mCancelButton = new JButton(mGuiAccessor.getImageIcon("driver.dialog.cancel.img"));
		mCancelButton.setToolTipText(mGuiAccessor.getString("driver.dialog.cancel"));
		mCancelButton.addActionListener(this);
		buttonPanel.add(mCancelButton);
		
		getContentPane().add(buttonPanel,BorderLayout.SOUTH);
		
		
		setSize(DISPLAY_WIDTH,DISPLAY_HEIGHT);
		setVisible(false);
		setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE);
		this.addWindowListener(this);
		fillList();
		if (mDriverListModel.size()>0) {
			mDriverDropDown.setSelectedIndex(0);
		}
	}

	/**
	 * Resets the list of drivers in the dropdown,
	 * using the ProfileManager.
	 */
	protected void fillList() {
		mDriverDropDown.clearSelection();
		mDriverListModel.clear();
		
		Set<String> profiles = mProfileManager.getDriverNames();
		for (String name : profiles) {
			DriverBean driver = mProfileManager.getDriver(name);
			mDriverListModel.addElement(driver);
		}
		
	}
	
	/**
	 * Checks if the entries have been changed by the user.
	 * This is used when the user closes the dialog, or 
	 * changes the driver type.
	 * 
	 * @return true if the driver does not match what was saved.
	 */
	protected boolean hasDriverChanged() {
		if (mDriver!=null) {
			return !mDriver.equals(buildDriver());
		}
		else {
			DriverBean driver = buildDriver();
			return driver.getName()!=null || driver.getClassname()!=null || driver.getUrlHint()!=null;
		}
	}
	
	/**
	 * Sets the dialog fields with the contents of the driverbean.
	 * 
	 * @param pIndex the index of the driver type into the driver type list
	 * @param pDriver the driver values to put into the fields.
	 */
	protected void setDriver(int pIndex,DriverBean pDriver) {
		mDriver = pDriver;
		mDriverIndex = pIndex;
		if (mDriver!=null) {
			mDriverDropDown.setSelectedValue(mDriver, true);
			mDriverField.setText(mDriver.getName());
			mClassnameField.setText(mDriver.getClassname());
			mUrlHintField.setText(mDriver.getUrlHint());
		}
		else {
			mDriverDropDown.clearSelection();
			mDriverField.setText(null);
			mClassnameField.setText(null);
			mUrlHintField.setText(null);
		}
	}
	
	/**
	 * Called when the dialog is being closed,
	 * it will ask the caller if they are sure
	 * when the driver contents have not been
	 * saved.  If they are, then it will close the
	 * dialog.
	 */
	protected void close() {
		if (hasDriverChanged()) {
			int answer = GuiManager.getInstance().question(mGuiAccessor.getString("driver.dialog.dirty"));
			if (answer!=JOptionPane.YES_OPTION) {
				mDriverDropDown.setSelectedIndex(mDriverIndex);
				return;
			}
		}
		mDriver = null;
		setVisible(false);
	}
	
	/**
	 * <p>Called when a button is pressed, it will deal
	 * with the handling of the button. </p>
	 * <p>For cancelling, it calls close().</p>
	 * <p>For saving, it validates the contents,
	 * and then saves the driver with the 
	 * ProfileManager.</p>
	 * <p>For deleting, it asks for confirmation,
	 * then deletes the driver with the ProfileManager.</p>
	 * <p>For new, it asks for confirmation if the current
	 * driver is dirty, and then creates a blank one.</p>
	 */
	public void actionPerformed(ActionEvent e) {
		Object obj = e.getSource();
		GuiManager gui = GuiManager.getInstance();
		if (obj.equals(mCancelButton)) {
			close();
		}
		else if (obj.equals(mSaveButton)) {
			DriverBean driver = buildDriver();
			if (driver.getName()==null) {
				gui.error(mGuiAccessor.getString("driver.dialog.missing.name"));
				return;
			}
			if (driver.getClassname()==null) {
				gui.error(mGuiAccessor.getString("driver.dialog.missing.class"));
				return;
			}
			try {
				ProfileManager.getInstance().setDriver(driver);
			}
			catch (IOException ex) {
				gui.error(mGuiAccessor.getString("driver.dialog.save.error",ex.getMessage()));
				return;
			}
			mDriver = ProfileManager.getInstance().getDriver(driver.getName());
			driver = mDriver;
			fillList();
			mDriverDropDown.setSelectedValue(driver, true);
			gui.info(mGuiAccessor.getString("driver.dialog.saved",driver.getName()));
		}
		else if (obj.equals(mDeleteButton)) {
			DriverBean driver = (DriverBean)mDriverDropDown.getSelectedValue();
			if (driver==null) {
				gui.error(mGuiAccessor.getString("driver.dialog.no.driver.selected"));
				return;
			}
			if (hasDriverChanged()) {
				int answer = GuiManager.getInstance().question(mGuiAccessor.getString("driver.dialog.dirty"));
				if (answer!=JOptionPane.YES_OPTION) {
					mDriverDropDown.setSelectedIndex(mDriverIndex);
					return;
				}
			}
			try {
				ProfileManager.getInstance().removeDriver(driver.getName());
			}
			catch (IOException ex) {
				gui.error(mGuiAccessor.getString("driver.dialog.delete.error",ex.getMessage()));
				return;
			}
			fillList();
			gui.info(mGuiAccessor.getString("driver.dialog.deleted",driver.getName()));
		}
		else if (obj.equals(mNewButton)) {
			if (hasDriverChanged()) {
				int answer = GuiManager.getInstance().question(mGuiAccessor.getString("driver.dialog.dirty"));
				if (answer!=JOptionPane.YES_OPTION) {
					mDriverDropDown.setSelectedIndex(mDriverIndex);
					return;
				}
			}
			fillList();
			DriverBean driver = ProfileManager.getInstance().createDefaultDriver();
			setDriver(-1, driver);
			
		}
	}

	/**
	 * Called with the driver list selection
	 * changes, it will check if the driver
	 * has changed first.  If so, it will ask
	 * for a confirmation.  It then updates
	 * the dialog with selected driver details.
	 */
	public void valueChanged(ListSelectionEvent e) {
		int listIndex = mDriverDropDown.getSelectedIndex();
		if (listIndex!=mDriverIndex) {
			if (hasDriverChanged()) {
				int answer = GuiManager.getInstance().question(mGuiAccessor.getString("driver.dialog.dirty"));
				if (answer!=JOptionPane.YES_OPTION) {
					mDriverDropDown.setSelectedIndex(mDriverIndex);
					return;
				}
			}
			
			DriverBean driver = null;
			if (listIndex>=0) {
				driver = (DriverBean)mDriverListModel.get(listIndex);
			}
			setDriver(listIndex,driver);
		}
	}

	/**
	 * Builds the driver from the edit fields.
	 * @return
	 */
	private DriverBean buildDriver() {
		DriverBean driver = new DriverBean();
		driver.setName(mDriverField.getText().trim());
		if (driver.getName().length()==0) driver.setName(null);
		driver.setClassname(mClassnameField.getText().trim());
		if (driver.getClassname().length()==0) driver.setClassname(null);
		driver.setUrlHint(mUrlHintField.getText().trim());
		if (driver.getUrlHint().length()==0) driver.setUrlHint(null);
		return driver;
	}
	
	/**
	 * This method starts the dialog.  Call this after
	 * creating the object.  It will return when the
	 * dialog is closed.
	 */
	public void display() {
		setLocationRelativeTo(getParent());
		setVisible(true);
	}

	/**
	 * Called as a WindowListener, it does nothing.
	 */
	public void windowOpened(WindowEvent e) {
	}

	/**
	 * Called when the user clicks the close button
	 * on the top bar of the dialog, it calls
	 * close(), just like the close button.
	 */
	public void windowClosing(WindowEvent e) {
		close();
	}

	/**
	 * Called as a WindowListener, it does nothing.
	 */
	public void windowClosed(WindowEvent e) {
	}

	/**
	 * Called as a WindowListener, it does nothing.
	 */
	public void windowIconified(WindowEvent e) {
	}

	/**
	 * Called as a WindowListener, it does nothing.
	 */
	public void windowDeiconified(WindowEvent e) {
	}

	/**
	 * Called as a WindowListener, it does nothing.
	 */
	public void windowActivated(WindowEvent e) {
	}

	/**
	 * Called as a WindowListener, it does nothing.
	 */
	public void windowDeactivated(WindowEvent e) {
	}

	/**
	 * From MouseAdapter.  Listens for clicks, and
	 * tries to set the selected prompt as the active
	 * prompt.
	 */
	class MouseHandler extends MouseAdapter{
		public void mouseClicked(MouseEvent e){
			if(e.getClickCount() >= 1) {
				int index = mDriverDropDown.locationToIndex(e.getPoint());
				if (index>=0) {
					mDriverDropDown.setSelectedIndex(index);
				}
				else {
					mDriverDropDown.clearSelection();
				}
			}
		}
	}

	/**
	 * Used to display the contents of the driver list, it sets
	 * the selected driver with a different background, and displays
	 * the icon.
	 */
	class ListItemRenderer extends JLabel implements ListCellRenderer {
		private ImageIcon profileIcon;
		
		public ListItemRenderer() {
			GuiNameAccessor guiAccessor = new GuiNameAccessor();
			profileIcon = new ImageIcon(guiAccessor.getImage("driver.icon"));
		}
		
		public Component getListCellRendererComponent(JList list,Object value,int index,
		boolean isSelected,boolean cellHasFocus) {
			DriverBean item = (DriverBean)value;
			setText(item.getName());
	        setIcon(profileIcon);
			if (isSelected) {
				setBackground(list.getSelectionBackground());
				setForeground(list.getSelectionForeground());
			}
			else {
				setBackground(list.getBackground());
				setForeground(list.getForeground());
			}
			setEnabled(list.isEnabled());
			setFont(list.getFont());
			setOpaque(true);
			return this;
		}
	}

}
